/****************************************************************************
 * Copyright (C) 2011 Maxim Integrated Products, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED PRODUCTS BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated Products
 * shall not be used except as stated in the Maxim Integrated Products
 * Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products retains all ownership rights.
 *
 *     Description: MAXQ Serial I/F (UART) Driver
 *             $Id: maxq_serial.c 1405 2011-06-09 19:45:45Z jbrodt $
 *
 ********************************************************************************
 */

#include "maxq_config.h"  // always include this file
#include "maxq_serial.h"
#include "maxq_serial_priv.h"
#include "stdlib.h"

#if (NUM_UART > 0)

/***** Definitions *****/
/**
 * Driver version number
 */
#define SERIAL_DRIVER_VERSION   (2)


/***** Function Prototypes *****/
uint8_t getparity(uint8_t value, SerialParity type);
uint8_t hasoddbits(uint8_t value);


/***** Global Data *****/
serial_port_t sp[NUM_UART];

// table to find out no. of bits in a nibble (used to calculate parity)
//                             0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
const unsigned char bits[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };


/******************************************************************************
*   Function Name : serial_version
*     Description : Returns the version number of this library
*        Input(s) : None
*       Output(s) : SERIAL_DRIVER_VERSION - Serial driver version number
*           Notes : The version number is defined in maxq_serial.h file.
*
******************************************************************************/
int serial_version(void)
{
  return SERIAL_DRIVER_VERSION;
}

/****************************************************************************
* Description: This function advances the internal serial library receive and
*   transmit character queues. It can be called from UART interrupt service
*   routine or periodically depending on the application requirements.
* Parameters: none
* Returns: void
****************************************************************************/
void serial_handler(void)
{
  uint8_t value;

  //***** UART0 *****
#if (NUM_UART > 0)
  if (SCON0 & SERIAL_TI) {
    SCON0 &= (~SERIAL_TI);     // clear the TI flag

    if (sp[0].txEnabled) {
      // Try to get next byte from FIFO
      if (fifo_get(&(sp[0].txf), &value)) {
        if ( (sp[0].parity != SERIAL_PARITY_NONE) && getparity(value, sp[0].parity) )
          SCON0 |= SERIAL_TB8;
        else
          SCON0 &= ~SERIAL_TB8;
        SBUF0 = value;
      }
      else
        sp[0].txStatus = 0; // transmission is complete
    }
  }

  if (SCON0 & SERIAL_RI) {
    SCON0 &= (~SERIAL_RI);    // clear RI

    // Get received byte
    value = SBUF0;

    // Check for parity error
    if ( (sp[0].parity != SERIAL_PARITY_NONE) &&
         (((SCON0 & 0x04) >> 2) != getparity(value, sp[0].parity)) )
        sp->parity_err_flag = 1;    // parity error

    // Put received byte in FIFO if not full, else drop the byte
    fifo_put(&(sp[0].rxf), value);
  }
#endif /* (NUM_UART > 0) */

  //***** UART1 *****
#if (NUM_UART > 1)
  if (SCON1 & SERIAL_TI) {
    SCON1 &= (~SERIAL_TI);     // clear the TI flag

    if (sp[1].txEnabled) {
      // Try to get next byte from FIFO
      if (fifo_get(&(sp[1].txf), &value)) {
        if ( (sp[1].parity != SERIAL_PARITY_NONE) && getparity(value, sp[1].parity) )
          SCON1 |= SERIAL_TB8;
        else
          SCON1 &= ~SERIAL_TB8;
        SBUF1 = value;
      }
      else
        sp[1].txStatus = 0; // transmission is complete
    }
  }

  if (SCON1 & SERIAL_RI) {
    SCON1 &= (~SERIAL_RI);    // clear RI

    // Get received byte
    value = SBUF1;

    // Check for parity error
    if ( (sp[1].parity != SERIAL_PARITY_NONE) &&
         (((SCON1 & 0x04) >> 2) != getparity(value, sp[1].parity)) )
        sp->parity_err_flag = 1;    // parity error

    // Put received byte in FIFO if not full, else drop the byte
    fifo_put(&(sp[1].rxf), value);
  }
#endif /* (NUM_UART > 1) */
}


/****************************************************************************
* Description: Computes the parity bit for the given data
* Parameters:
*  - value: data to compute parity for
*  - type: type of parity
* Returns: parity bit
****************************************************************************/
uint8_t getparity(uint8_t value, SerialParity type)
{
  uint8_t parity;

  switch (type)
  {
    case SERIAL_PARITY_MARK:
      parity = 1;  // always 1
      break;
    case SERIAL_PARITY_SPACE:
      parity = 0;  // always 0
      break;
    case SERIAL_PARITY_ODD:
      if (hasoddbits(value))
        parity = 0;
      else
        parity = 1;
      break;
    case SERIAL_PARITY_EVEN:
      if (hasoddbits(value))
        parity = 1;
      else
        parity = 0;
      break;
    default:
      parity = 0;
  }
  
  return parity;
}


/****************************************************************************
* Description: Checks if the data has an odd number of set bits
* Parameters: data to check
* Returns: 1 if data has an odd number of set bits, 0 otherwise
****************************************************************************/
uint8_t hasoddbits(uint8_t value)
{
  uint8_t count;
  count = bits[value & 0x0F] + bits[(value & 0xF0) >> 4];
  return (count % 2);
}

#endif /* (NUM_UART > 0) */
